﻿using CMS.Controls.Messages;
using CMS.Infos;
using CMS.REST;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace CMS.Controls.TabPageTemplate
{
    public abstract class EditorAdaptor : IMessageSource
    {
        #region 枚举类型
        /// <summary>
        /// 数据控制三种形态
        /// </summary>
        public enum DisplayMode
        {
            New,        //所有字段应该都可以写入
            Edit,       //可能只有一部分可以修改
            Preview,    //所有字段都不应该被修改
            None        //所有字段应该表示为空状态
        }
        #endregion

        #region 待实现的抽象属性

        #region 元素类型
        /// <summary>
        /// 元素类型
        /// </summary>
        public abstract Type ElementType { get; }
        #endregion

        #region 显示/可视化相关
        /// <summary>
        /// 元素的名称
        /// </summary>
        public abstract string ItemName { get; }
        #endregion

        #region 服务器请求相关
        /// <summary>
        /// RESTFul API,用于请求数据的默认实现
        /// </summary>
        public abstract string API { get; }

        /// <summary>
        /// 元素主键字段的名称
        /// </summary>
        public abstract string PkField { get; }

        /// <summary>
        /// 元素名称字段的名称
        /// </summary>
        public abstract string NameField { get; }
        #endregion

        #region 权限控制相关
        /// <summary>
        /// 允许读取数据
        /// </summary>
        public abstract bool AllowIndexItem { get; }

        /// <summary>
        /// 允许新增数据
        /// </summary>
        public abstract bool AllowCreateItem { get; }

        /// <summary>
        /// 允许更新数据,将会影响编辑按钮是否可用
        /// </summary>
        public abstract bool AllowUpdateItem { get; }

        /// <summary>
        /// 允许删除数据
        /// </summary>
        public abstract bool AllowDeleteItem { get; }
        #endregion

        #endregion

        #region 数据源
        /// <summary>
        /// 主数据列表数据源(用于给ListBox预览框绑定)
        /// </summary>
        public BindingSource MainListSource { get; } = new BindingSource();

        /// <summary>
        /// 编辑器数据绑定
        /// </summary>
        public BindingSource EditorSource { get; } = new BindingSource();


        #endregion

        #region 事件
        /// <summary>
        /// 发送消息事件
        /// </summary>
        public event EventHandler<MessageSentEventArgs> MessageSending;
        #endregion

        #region 工具函数
        /// <summary>
        /// 空对象数组,用于调用get方法
        /// </summary>
        protected static readonly object[] EmptyObjectArray = new object[0];

        /// <summary>
        /// 获取本适配器匹配到的类型的数据的新对象(相当于new T())
        /// </summary>
        /// <returns></returns>
        protected object _NewItemInstance()
        {
            return Activator.CreateInstance(ElementType);
        }

        /// <summary>
        /// 获取本适配器匹配到的类型的数据的新数组对象,相当于(new T[0])
        /// </summary>
        /// <returns></returns>
        protected object _NewItemArrayInstance()
        {
            return Activator.CreateInstance(ElementType.MakeArrayType(1));
        }

        /// <summary>
        /// 获取对象的主键的值
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        protected object _GetPkValue(object obj)
        {
            PropertyInfo property = ElementType.GetProperty(PkField);
            return property.GetMethod.Invoke(obj, new object[0]);
        }

        /// <summary>
        /// 设置对象中的某一个属性的值
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected object _SetPropertyValue(object obj, string propName, object value)
        {
            PropertyInfo property = ElementType.GetProperty(propName);
            property.SetMethod.Invoke(obj, new object[] { value });
            return obj;
        }

        /// <summary>
        /// 获取一个元素的副本,如果获取失败,则将会以新元素返回
        /// </summary>
        /// <param name="srcObj"></param>
        /// <returns></returns>
        protected object _CopyItem(object srcObj)
        {
            if (srcObj is ICloneable src)
            {
                return src.Clone();
            }
            else
            {
                return _NewItemInstance();
            }
        }

        /// <summary>
        /// 为了使用统一的FormPanel,应采用了此函数来进行统一配置FormPanel
        /// </summary>
        /// <param name="editorPanel"></param>
        /// <param name="formPanel"></param>
        protected void _SettingFormPanel(Panel editorPanel, FormPanel formPanel)
        {
            FormBase.LayoutDetial layout = formPanel.LayoutD;
            layout.Columns[0].Width = 100;
            layout.Columns[1].Width = 250;
            layout.Columns[2].Width = 70;
            layout.RowHeight = 50;
            formPanel.LayoutD = layout;
            formPanel.Size = new Size(420, 500);
            formPanel.Location = new Point(editorPanel.Padding.Left, editorPanel.Padding.Top);
            formPanel.Name = "FP_Main";
            if (!editorPanel.Contains(formPanel))
            {
                editorPanel.Controls.Add(formPanel);
            }
        }

        /// <summary>
        /// 发送消息函数
        /// </summary>
        /// <param name="msgType"></param>
        /// <param name="message"></param>
        protected void _SendMessage(MsgType msgType, string message)
        {
            MessageSending?.Invoke(this, new MessageSentEventArgs(msgType, message));
        }

        /// <summary>
        /// 强制将已编辑的项的数据到主列表(此函数慎用,可能会破坏数据的同步性)
        /// </summary>
        /// <returns></returns>
        protected bool _ForceUpdateMainList()
        {
            if (_EditingData == null)
            {
                return false;
            }
            else
            {
                if (_EditingData is IAssignable assign)
                {
                    assign.Assign(EditorSource.DataSource);
                    MainListSource.ResetCurrentItem();
                    EditorSource.ResetCurrentItem();
                    return true;
                }
                else
                {
                    throw new Exception("ElementType必须实现IAssignable接口");
                }
            }
        }

        #endregion

        public EditorAdaptor()
        {
            EditorSource.DataSource = _NewItemInstance();
        }

        #region 待实现的抽象函数
        /// <summary>
        /// 在此函数中实现编辑器的初始化,包括将数据绑定上去
        /// </summary>
        public abstract void InitializeEditor(Panel editorPanel, BindingSource source);

        /// <summary>
        /// 更新用户自定义组件的显示模式,请在此函数中对控件的状态进行修改,以达到自由控制控件的可用性的目的
        /// </summary>
        /// <param name="editorPanel"></param>
        /// <param name="mode"></param>
        /// <returns></returns>
        public abstract bool UpdateDisplayMode(Panel editorPanel, DisplayMode mode);

        /// <summary>
        /// 检查表单是否合法
        /// </summary>
        /// <returns></returns>
        public abstract bool ValidateForm();
        #endregion

        #region 提供给TabPage进行调用接口
        /// <summary>
        /// 当前正在编辑的数据的原始数据,用于同步
        /// </summary>
        private object _EditingData = null;



        /// <summary>
        /// 当TabPage请求刷新元素列表时,将立即调用此接口,剩下的由适配器进行处理
        /// </summary>
        public void OnRefrshList()
        {
            MainListSource.DataSource = LoadList();
        }

        /// <summary>
        /// 当TabPage请求新增一个元素时,将立即调用此接口,由适配器进行数据处理
        /// </summary>
        public void OnCreateItem(Panel editorPanel)
        {
            EditorSource.DataSource = _NewItemInstance();
            _EditingData = null;
            UpdateDisplayMode(editorPanel, DisplayMode.New);
        }

        /// <summary>
        /// 当TabPage请求编辑一个元素时,将立即调用此接口,由适配器进行数据处理
        /// </summary>
        /// <param name="editorPanel"></param>
        public void OnEditItem(Panel editorPanel, object obj)
        {
            EditorSource.DataSource = _CopyItem(obj);
            _EditingData = obj;
            UpdateDisplayMode(editorPanel, DisplayMode.Edit);
        }

        /// <summary>
        /// 当TabPage请求显示一个元素时,将立即调用此接口,由适配器进行数据处理
        /// </summary>
        /// <param name="editorPanel"></param>
        /// <param name="editObj"></param>
        public void OnDisplayItem(Panel editorPanel, object obj)
        {
            EditorSource.DataSource = _CopyItem(obj);
            _EditingData = obj;
            UpdateDisplayMode(editorPanel, DisplayMode.Preview);
        }

        /// <summary>
        /// 当TabPage请求删除一个元素时,将立即调用此接口,由适配器进行处理
        /// </summary>
        /// <param name="editorPanel"></param>
        /// <param name="obj"></param>
        public void OnDeleteItems(object[] objs)
        {
            MethodInfo method = ElementType.GetProperty(NameField).GetMethod;
            object[] array = (from obj in objs select method.Invoke(obj, EmptyObjectArray)).ToArray();
            string arrayString = string.Join("、", array);
            if (arrayString.Length > 20)
            {
                arrayString = arrayString.Substring(0, 20) + "...";
            }
            if (MessageBox.Show($"您确认要删除[{arrayString}]这{array.Length}个{ItemName}数据吗？此操作不可逆。", "删除确认", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
            {
                if (DeleteItems(objs, out string message))
                {
                    foreach (object obj in objs)
                    {
                        MainListSource.Remove(obj);
                    }
                    _SendMessage(MsgType.OK, $"删除{arrayString}成功");
                }
                else
                {
                    _SendMessage(MsgType.ERROR, $"删除{arrayString}失败:{message}");
                }
            }
        }

        /// <summary>
        /// 当TabPage请求禁用编辑器时,将立即调用此接口,由适配器进行处理
        /// </summary>
        /// <param name="editorPanel"></param>
        public void OnDisableEditor(Panel editorPanel)
        {
            UpdateDisplayMode(editorPanel, DisplayMode.None);
        }

        /// <summary>
        /// 当TabPage请求保存一个数据时,将立即调用此接口
        /// </summary>
        /// <param name="obj"></param>
        public void OnSaveItem()
        {
            if (!ValidateForm())
            {
                _SendMessage(MsgType.ERROR, "数据不合法,请修正后重试");
                return;
            }
            if (!Equals(_EditingData, EditorSource.DataSource))
            {
                if (_EditingData == null)
                {
                    //新增数据
                    if (CreateItem(EditorSource.DataSource, out string message))
                    {
                        MainListSource.Add(EditorSource.DataSource);
                        MainListSource.MoveLast();
                        _SendMessage(MsgType.OK, $"新增{ItemName}成功");
                    }
                    else
                    {
                        _SendMessage(MsgType.ERROR, $"新增{ItemName}失败:{message}");
                    }
                }
                else
                {
                    //更新数据
                    if (Equals(_GetPkValue(_EditingData), _GetPkValue(EditorSource.DataSource)))
                    {
                        if (UpdateItem(EditorSource.DataSource, out string message))
                        {
                            if (_EditingData is IAssignable data)
                            {
                                data.Assign(EditorSource.DataSource);
                                EditorSource.ResetCurrentItem();
                                MainListSource.ResetCurrentItem();
                                _SendMessage(MsgType.OK, $"更新{ItemName}成功");
                            }
                            else
                            {
                                throw new Exception("适配器的类型必须支持IAssignable接口");
                            }
                        }
                        else
                        {
                            _SendMessage(MsgType.ERROR, $"更新{ItemName}失败:{message}");
                        }
                    }
                    else
                    {
                        throw new Exception("编辑元素时,主键不应当发生改变,发现此异常时,请及时联系作者");
                    }
                }
            }
            else
            {
                _SendMessage(MsgType.WARN, "不需要保存,因为未做任何修改");
            }
        }

        #endregion

        #region 数据相关,后端交互,列表维护
        /// <summary>
        /// 读取列表
        /// </summary>
        /// <returns></returns>
        protected virtual object LoadList()
        {
            RESTResponse response = RESTClient.IndexManaged(API);
            if (response.IsSuccess)
            {
                return new List<object>((object[])response.Data.ToObject(ElementType.MakeArrayType()));
            }
            else
            {
                return new List<object>();
            }
        }

        /// <summary>
        /// 向服务器提交数据新增,如果新增数据成功,则根据拿到的信息完善对象
        /// </summary>
        /// <param name="obj">要被提交的对象</param>
        /// <returns>返回是否成功</returns>
        protected virtual bool CreateItem(object obj, out string message)
        {
            RESTResponse response = RESTClient.SaveManaged(API, JObject.FromObject(obj));
            message = response.ErrMsg;
            if (response.IsSuccess)
            {
                PropertyInfo property = ElementType.GetProperty(PkField);
                property.SetMethod.Invoke(obj, (from i in response.Data select i.ToObject(property.GetMethod.ReturnType)).ToArray());
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 向服务器提交数据更改,如果更新成功返回true
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        protected virtual bool UpdateItem(object obj, out string message)
        {
            RESTResponse response = RESTClient.UpdateManaged(API, _GetPkValue(obj).ToString(), JObject.FromObject(obj));
            message = response.ErrMsg;
            return response.IsSuccess;
        }

        /// <summary>
        /// 向服务器提交多个数据删除,如果删除成功返回true
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        protected virtual bool DeleteItems(object[] objs, out string message)
        {
            MethodInfo getMethod = ElementType.GetProperty(PkField).GetMethod;
            string[] ids = (from obj in objs select getMethod.Invoke(obj, new object[0]).ToString()).ToArray();
            RESTResponse response = RESTClient.DeletesManaged(API, ids);
            message = response.ErrMsg;
            return response.IsSuccess;
        }



        #endregion
    }
}
